{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "635d8ebb",
   "metadata": {},
   "source": [
    "# Structured Output Chain \n",
    "\n",
    "- Author: [JeongHo Shin](https://github.com/ThePurpleCollar)\n",
    "- Peer Review: \n",
    "- Proofread : [Juni Lee](https://www.linkedin.com/in/ee-juni)\n",
    "- This is a part of [LangChain Open Tutorial](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial)\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/14-Chains/03-Structured-Output-Chain.ipynb)",
    "[![Open in GitHub](https://img.shields.io/badge/Open%20in%20GitHub-181717?style=flat-square&logo=github&logoColor=white)](https://github.com/LangChain-OpenTutorial/LangChain-OpenTutorial/blob/main/14-Chains/03-Structured-Output-Chain.ipynb)",
    "\n",
    "## Overview\n",
    "\n",
    "This tutorial demonstrates how to implement structured output generation using LangChain and OpenAI's language models.\n",
    "\n",
    "We'll build a quiz generation system that creates multiple-choice questions with consistent formatting and structure.\n",
    "\n",
    "\n",
    "### Table of Contents\n",
    "\n",
    "- [Overview](#overview)\n",
    "- [Environment Setup](#environment-setup)\n",
    "- [Implementing Structured Output Chain](#implementing-structured-output-chain)\n",
    "- [Invoking the Chain](#invoking-the-chain)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6c7aba4",
   "metadata": {},
   "source": [
    "## Environment Setup\n",
    "\n",
    "Setting up your environment is the first step. See the [Environment Setup](https://wikidocs.net/257836) guide for more details.\n",
    "\n",
    "\n",
    "**[Note]**\n",
    "\n",
    "The langchain-opentutorial is a package of easy-to-use environment setup guidance, useful functions and utilities for tutorials.\n",
    "Check out the  [```langchain-opentutorial```](https://github.com/LangChain-OpenTutorial/langchain-opentutorial-pypi) for more details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "21943adb",
   "metadata": {},
   "outputs": [],
   "source": [
    "%%capture --no-stderr\n",
    "%pip install langchain-opentutorial"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f25ec196",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install required packages\n",
    "from langchain_opentutorial import package\n",
    "\n",
    "package.install(\n",
    "    [\n",
    "        \"langsmith\",\n",
    "        \"langchain\",\n",
    "        \"langchain_core\",\n",
    "        \"langchain-anthropic\",\n",
    "        \"langchain_community\",\n",
    "        \"langchain_text_splitters\",\n",
    "        \"langchain_openai\",\n",
    "    ],\n",
    "    verbose=False,\n",
    "    upgrade=False,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "690a9ae0",
   "metadata": {},
   "source": [
    "You can set API keys in a ```.env``` file or set them manually.\n",
    "\n",
    "[Note] If you’re not using the ```.env``` file, no worries! Just enter the keys directly in the cell below, and you’re good to go."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "327c2c7c",
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from langchain_opentutorial import set_env\n",
    "\n",
    "# Attempt to load environment variables from a .env file; if unsuccessful, set them manually.\n",
    "if not load_dotenv():\n",
    "    set_env(\n",
    "        {\n",
    "            \"OPENAI_API_KEY\": \"\",\n",
    "            \"LANGCHAIN_API_KEY\": \"\",\n",
    "            \"LANGCHAIN_TRACING_V2\": \"true\",\n",
    "            \"LANGCHAIN_ENDPOINT\": \"https://api.smith.langchain.com\",\n",
    "            \"LANGCHAIN_PROJECT\": \"Structured-Output-Chain\", \n",
    "        }\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aa00c3f4",
   "metadata": {},
   "source": [
    "## Implementing Structured Output Chain\n",
    "\n",
    "This tutorial walks you through the process of generating 4-option multiple-choice quizzes for a given topic.\n",
    "\n",
    "The ```Quiz``` class defines the structure of the quiz, including the question, difficulty level, and four answer options.\n",
    "\n",
    "A ```ChatOpenAI``` instance leverages the **GPT-4o** model for natural language processing, while a ```ChatPromptTemplate``` specifies the conversational instructions for generating the quizzes dynamically."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "ea361da4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Import required modules and libraries\n",
    "from langchain.chains.openai_functions import create_structured_output_runnable\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from pydantic import BaseModel, Field\n",
    "from typing import List\n",
    "\n",
    "# Define the Quiz class - Represents the structure of a 4-option multiple-choice quiz\n",
    "class Quiz(BaseModel):\n",
    "    \"\"\"Extracts information for a 4-option multiple-choice quiz\"\"\"\n",
    "\n",
    "    question: str = Field(..., description=\"The quiz question\")  # Quiz question\n",
    "    level: str = Field(\n",
    "        ..., description=\"The difficulty level of the quiz (easy, medium, hard)\"\n",
    "    )\n",
    "    options: List[str] = Field(..., description=\"The 4 answer options for the quiz\")  # Answer options\n",
    "\n",
    "\n",
    "# Set up the GPT-4o model with appropriate parameters\n",
    "llm = ChatOpenAI(model=\"gpt-4o\", temperature=0.1)\n",
    "\n",
    "# Define a prompt template to guide the model in generating quizzes\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"You're a world-famous quizzer and generate quizzes in structured formats.\",\n",
    "        ),\n",
    "        (\n",
    "            \"human\",\n",
    "            \"Please create a 4-option multiple-choice quiz related to the topic provided below. \"\n",
    "            \"If possible, base the question on existing trivia but do not directly include details from the topic in the question. \"\n",
    "            \"\\nTOPIC:\\n{topic}\",\n",
    "        ),\n",
    "        (\"human\", \"Tip: Make sure to answer in the correct format\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "# Create a structured output model to match the Quiz class structure\n",
    "llm_with_structured_output = llm.with_structured_output(Quiz)\n",
    "\n",
    "# Combine the prompt and the structured output model into a single chain\n",
    "chain = prompt | llm_with_structured_output"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e56c8b97",
   "metadata": {},
   "source": [
    "## Invoking the Chain"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "609faf0a",
   "metadata": {},
   "source": [
    "In this section, we demonstrate how to invoke the **structured output chain** to generate quizzes dynamically. The chain combines a prompt template and a structured output model to ensure the output adheres to the desired ```Quiz``` structure."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "69cb77da",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Request the generation of a quiz based on a given topic\n",
    "generated_quiz = chain.invoke({\"topic\": \"Korean Food\"})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b358b3bc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Which of the following is a traditional Korean dish made by fermenting vegetables, primarily napa cabbage and Korean radishes, with a variety of seasonings? (Difficulty: medium)\n",
      "\n",
      "1) Kimchi\n",
      "2) Sushi\n",
      "3) Tacos\n",
      "4) Paella\n"
     ]
    }
   ],
   "source": [
    "# Print the generated quiz\n",
    "print(f\"{generated_quiz.question} (Difficulty: {generated_quiz.level})\\n\")\n",
    "for i, opt in enumerate(generated_quiz.options):\n",
    "    print(f\"{i+1}) {opt}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-opentutorial-W8hXPYms-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}